#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "tinyFS.h"

typedef struct Disk {
   char *filename;
   FILE *fp;
   int id;
   int space;
   struct Disk *next;
} Disk;
int size = 0;
Disk *root = NULL, *current = NULL;

Disk *checkPresent(char *filename) {
   int i;
   Disk *curr = root;
   for(i = 0; i < size; i++) {
      if(strcmp(curr->filename, filename) == 0)
         break;
      curr = curr->next;
   }
   return curr;
}

Disk *getDisk(int diskNo) {
   int i;
   Disk *curr = root;
   for(i = 0; i < size; i++) {
      if(curr->id == diskNo)
         break;
      curr = curr->next;
   }
   return curr;
}

/* This functions opens a regular UNIX file and designates the first nBytes of
it as space for the emulated disk. nBytes should be an integral number of the
block size.  */
int openDisk(char *filename, int nBytes) {
   FILE *fp;
   Disk *found = NULL; /*disk number of found disk*/
   int val = -1;

   if(nBytes % BLOCKSIZE != 0) {
      printf("Error, bad blocksize.\n");
      return -1;
   }

   if(root != NULL)
      found = checkPresent(filename); //go through list see if file there

   //check disk list
   /*If nBytes > 0 and there is already a file by the given filename,
   that file’s content may be overwritten.*/
   if(nBytes > 0 && found) { //overwrite existing
      found->space = nBytes;
      val = found->id;
   }
   else if(nBytes > 0) {
      Disk *new = malloc(sizeof(Disk));
      new->filename = malloc(strlen(filename));
      strcpy(new->filename, filename);
      new->id = size++;
      new->fp = fopen(filename, "w+");
      new->space = nBytes;
      new->next = NULL;
      if(root) {
         current->next = new;
         current = new;
      } else {
         root = new;
         current = root;
      }
      val = new->id;
   }
   /*If nBytes is 0, an existing disk is opened, and should not be overwritten.*/
   else if (nBytes == 0){
      val = found->id;
   }

   /*The return value is -1 on failure or a disk number on success.*/
   return val;
}

/* readBlock() reads an entire block of BLOCKSIZE bytes from the open disk
(identified by ‘disk’) and copies the result into a local buffer (must be at
least of BLOCKSIZE bytes).  */
int readBlock(int disk, int bNum, void *block){
   /*The bNum is a logical block number, which must be
   translated into a byte offset within the disk. The translation from logical to
   physical block is straightforward: bNum=0 is the very first byte of the file.
   bNum=1 is BLOCKSIZE bytes into the disk, bNum=n is n*BLOCKSIZE bytes into the
   disk.*/
   int byteNum = bNum * 256, val = -1;
   Disk *foundDisk;
   if(disk >= size) {
      printf("Error no. -2, bad disk number\n");
      return -2;
   }
   foundDisk = getDisk(disk);
   if(byteNum >= foundDisk->space) {
      printf("Error no. -3, bad block number\n");
      return -3;
   }
   // if(sizeof(block) >= BLOCKSIZE){
   //    printf("Error no. -5, bad blocksize in buffer\n");
   //    return -4;
   // }

   fseek(foundDisk->fp, byteNum, SEEK_SET);
   if(fread(block , BLOCKSIZE, 1, foundDisk->fp))
      val = 0;

   /*On success, it returns 0. -1 or smaller is returned if disk is not
   available (hasn’t been opened) or any other failures. You must define your
   own error code system. */
   return val;
}

/* writeBlock() takes disk number ‘disk’ and logical block number ‘bNum’ and
writes the content of the buffer ‘block’ to that location. ‘block’ must be
integral with BLOCKSIZE. Just as in readBlock(), writeBlock() must translate
the logical block bNum to the correct byte position in the file. On success,
it returns 0. -1 or smaller is returned if disk is not available (i.e. hasn’t
been opened) or any other failures. You must define your own error code system.
*/
int writeBlock(int disk, int bNum, void *block) {
   FILE *fp;
   Disk *found;
   int byteNum;
   // if(strlen(block) % BLOCKSIZE != 0){
   //    printf("Error no. -4, bad blocksize in buffer\n");
   //    return -4;
   // }
   if(disk >= size) {
      printf("Error no. -2, bad disk number\n");
      return -2;
   }
   found = getDisk(disk);
   byteNum = 256 * bNum;
   if(byteNum >= found->space) {
      printf("Error no. -3, bad block number\n");
      return -3;
   }

   fseek(found->fp, byteNum, SEEK_SET);
   fwrite(block, BLOCKSIZE, 1, found->fp);
   return 0;
}

// Checks if disk exists, if set up correctly, and returns the disk id
int verify(char *filename) {
   Disk *tmp = NULL;
   int i;
   char buffer[256] = "";
   if(root) {
      //Find it first
      tmp = checkPresent(filename);
      if(tmp == NULL)
         return -1;
      //check type and magin numbers
      for(i = 0; i < 40; i++) {
         readBlock(tmp->id, i, buffer);
         if(!((buffer[0] == 1 || buffer[0] == 2 || buffer[0] == 3 || buffer[0] == 4) && buffer[1] == '\x45')) {
            printf("invalid disk\n");
            return -1;
         }
      }
      return tmp->id;
   } else
      return -1;
}
